package com.ruoyi.project.payUtils.wx;

import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.druid.support.json.JSONUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.project.content.FlowTypeEnum;
import com.ruoyi.project.payUtils.wx.sdk.WXPay;
import com.ruoyi.project.payUtils.wx.sdk.WXPayConstants;
import com.ruoyi.project.payUtils.wx.sdk.WXPayUtil;
import com.ruoyi.project.payUtils.wx.sdk.WxConfig;
import com.ruoyi.project.payUtils.wx.sdk.WxPaymentConfig;
import com.ruoyi.project.payUtils.wx.sdk.WxXcxConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

@Component
public class WxPayUtils {

    private static final Logger log = LoggerFactory.getLogger(WxPayUtils.class);

    @Value ("${wx.xcx.appid}")
    private String xcxAppid;

    @Value ("${wx.xcx.secretid}")
    private String xcxSecret;

    @Value ("${pay.wx.notifyUrl}")
    public String notifyUrl;

    @Value ("${pay.wx.refundNotifyUrl}")
    public String refundNotifyUrl;

    @Autowired
    private WxConfig wxConfig;
    @Autowired
    private WxXcxConfig  wxXcxConfig;

    @Autowired
    private WxPaymentConfig wxPaymentConfig;

    /**
     * 支付返回 支持app和小程序
     *
     * @param outTradeNo
     * @param flowTypeEnum
     * @param money
     * @param ip
     * @param createTime
     * @param tradeType
     * @param openId
     *
     * @return
     *
     * @throws Exception
     */
    public Map <String, String> getPayResult (String outTradeNo, FlowTypeEnum flowTypeEnum, BigDecimal money, String ip,
            Date createTime, String tradeType, String openId, Long userId, String userName) throws Exception {
        // 原生支付


        //根据微信支付api来设置
        Map <String, String> data = new HashMap <>();
        WXPay wxPay = new WXPay(this.wxPaymentConfig);
        String key = this.wxPaymentConfig.getKey();
        String appid = this.wxPaymentConfig.getAppID();
        String mchid = this.wxPaymentConfig.getMchID();

        data.put("appid", appid);
        //商户号
        data.put("mch_id", mchid);
        //支付场景 APP 微信app支付 JSAPI 公众号支付  NATIVE 扫码支付
        data.put("trade_type", tradeType);
        if ("JSAPI".equals(tradeType)) {
            //JSAPI必须要openId
            log.debug("customXiaoAppid:" + this.wxPaymentConfig.getXcxAppid());
            data.put("appid", this.wxPaymentConfig.getXcxAppid());
            data.put("openid", openId);
        }
        //回调地址
        data.put("notify_url", this.wxPaymentConfig.getNotifyUrl());
        //终端ip
        data.put("spbill_create_ip", ip);
        // 测试默认1分钱
        //data.put("total_fee","1");
        //订单总金额
        data.put("total_fee", money.setScale(0, RoundingMode.HALF_UP).toString());
        //默认人民币
        data.put("fee_type", "CNY");
        //outTradeNo = flowTypeEnum.getValue() + "-" + outTradeNo + "-" + DateUtils.getNowDate().getTime();
        //交易号
        data.put("out_trade_no", outTradeNo);
        //记录交易号

        data.put("body", "找零工平台支付");
        data.put("device_info", "WEB");
        data.put("time_expire", DateUtils.parseDateToStr("yyyyMMddHHmmss", DateUtils.addMinutes(createTime, 29)));
        // 随机字符串小于32位
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        //签名
        String s = WXPayUtil.generateSignature(data, key);
        data.put("sign", s);
        // wxPay.unifiedOrder 这个方法中调用微信统一下单接口
        Map <String, String> respData = wxPay.unifiedOrder(data);

        log.info("获取传输参数:" + JSONUtils.toJSONString(data));
        log.info("获取预签名返回参数:" + JSONUtils.toJSONString(respData));

        if ("SUCCESS".equals(respData.get("return_code"))) {
            //返回给APP端的参数，APP端再调起支付接口
            Map <String, String> repData = new HashMap <>();

            if ("JSAPI".equals(tradeType)) {
                //JSAPI
                repData.put("appId", this.wxPaymentConfig.getXcxAppid());
                repData.put("package", "prepay_id=" + respData.get("prepay_id"));
                repData.put("nonceStr", respData.get("nonce_str"));
                repData.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
                repData.put("signType", WXPayConstants.MD5);
            } else {
                repData.put("appid", appid);
                repData.put("partnerid", mchid);
                repData.put("package", "Sign=WXPay");
                repData.put("prepayid", respData.get("prepay_id"));
                repData.put("noncestr", respData.get("nonce_str"));
                repData.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
            }
            log.debug("二次签名参数：" + JSONUtils.toJSONString(repData));

            //签名
            String sign = WXPayUtil.generateSignature(repData, key);
            repData.put("sign", sign);



            return repData;
        } else {
            log.info("预签名返回错误:" + JSONUtils.toJSONString(respData));
            throw new RuntimeException(respData.get("return_msg"));
        }
    }


    public String generateSign (Map <String, String> param) throws Exception {
        String sign = param.get("sign");
        param.remove("sign");
        String riderSign = WXPayUtil.generateSignature(param, this.wxConfig.getKey());
        param.put("sign", sign);
        return riderSign;
    }

    /**
     * 退款
     *
     * @param outTradeNo
     * @param outRefundNo
     * @param amount
     */
    public Map <String, String> refund (String outTradeNo, String outRefundNo, BigDecimal amount, Integer count) {
        // 第三方退款
        //        return leshuaPayUtil.refund(outRefundNo);

        // 原生退款
        try {
            Map <String, String> data = new HashMap <>();
            WXPay wxPay = new WXPay(this.wxPaymentConfig);
            String key = this.wxPaymentConfig.getKey();
            String appid = this.wxPaymentConfig.getAppID();
            String mchid = this.wxPaymentConfig.getMchID();

            data.put("appid", appid);
            //商户号
            data.put("mch_id", mchid);
            data.put("nonce_str", WXPayUtil.generateNonceStr());
            data.put("out_trade_no", outTradeNo);
            data.put("out_refund_no", outRefundNo);
            data.put("total_fee", amount.setScale(0, RoundingMode.HALF_UP).toString());
            data.put("refund_fee", amount.setScale(0, RoundingMode.HALF_UP).toString());
            data.put("notify_url", this.wxPaymentConfig.getRefundNotifyUrl());
            //签名
            String s = WXPayUtil.generateSignature(data, key);
            data.put("sign", s);
            // wxPay.refund 这个方法中调用微信统一退款接口
            Map <String, String> resultMap = wxPay.refund(data);
            log.info("获取传输参数:" + JSONUtils.toJSONString(data));
            log.info("获取预签名返回参数:" + JSONUtils.toJSONString(resultMap));

            // 下单失败，进行处理
            if (WXPayConstants.FAIL.equals(resultMap.get("return_code"))) {

                // 处理结果返回，无需继续执行
                resultMap.put("return_code", WXPayConstants.FAIL);
                resultMap.put("return_msg", resultMap.get("return_msg"));
                return resultMap;
            }

            if (WXPayConstants.FAIL.equals(resultMap.get("result_code"))) {
                resultMap.put("return_code", WXPayConstants.FAIL);
                resultMap.put("return_msg", resultMap.get("err_code_des"));
            }

            return resultMap;

        } catch (Exception e) {
            log.error("微信退款错误:" + e.getMessage());
            if (count < 5) {
                return this.refund(outTradeNo, outRefundNo, amount, ++count);
            } else {
                Map <String, String> resultMap = new HashMap <>();
                resultMap.put("return_code", WXPayConstants.FAIL);
                resultMap.put("return_msg", "退款错误:" + e.getMessage());
                return resultMap;
            }
        }
    }

    /**
     * 微信提现
     *
     * @param account
     * @param userName
     * @param withdrawalId
     * @param amount
     */
    public void cashWithdrawal (String account, String userName, String withdrawalId, BigDecimal amount)
            throws Exception {
        WXPay wxPay = new WXPay(this.wxConfig);
        String key = this.wxConfig.getKey();
        String appid = this.wxConfig.getAppID();
        String mchid = this.wxConfig.getMchID();
        Map <String, String> data = new HashMap <>();
        data.put("mch_appid", appid);
        //商户号
        data.put("mchid", mchid);
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("partner_trade_no", withdrawalId);
        data.put("openid", account);
        data.put("check_name", "FORCE_CHECK");
        data.put("re_user_name", userName);

        data.put("amount", amount.setScale(0, RoundingMode.HALF_UP).toString());
        data.put("desc", "找零工提现");
        //签名
        String s = WXPayUtil.generateSignature(data, key);
        data.put("sign", s);
        /** wxPay.transfer 这个方法中调用微信转账接口 */
        Map <String, String> resultMap = wxPay.transfer(data);
        log.info("获取传输参数:" + JSONUtils.toJSONString(data));

        // 下单失败，进行处理
        if (WXPayConstants.FAIL.equals(resultMap.get("return_code"))) {
            throw new RuntimeException("微信提现失败:" + resultMap.get("return_msg"));
        }

        if (WXPayConstants.FAIL.equals(resultMap.get("result_code"))) {
            //            if (WXPayConstants.SYSTEMERROR.equals(resultMap.get("err_code"))){
            //                cashWithdrawal(account,userName,withdrawalId,amount);
            //                return;
            //            }
            throw new RuntimeException("微信提现失败:" + resultMap.get("err_code_des"));
        }
    }


    /**
     * 微信提现
     *
     * @param account
     * @param userName
     * @param withdrawalId
     * @param amount
     */
    public void cashWithdrawalTest (String account, String userName, String withdrawalId, BigDecimal amount)
            throws Exception {
        WXPay wxPay = new WXPay(this.wxXcxConfig);
        String key = this.wxXcxConfig.getKey();
        String appid = this.wxXcxConfig.getAppID();
        String mchid = this.wxXcxConfig.getMchID();
        Map <String, String> data = new HashMap <>();
        data.put("mch_appid", appid);
        //商户号
        data.put("mchid", mchid);
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("partner_trade_no", withdrawalId);
        data.put("openid", account);
        data.put("check_name", "FORCE_CHECK");
        data.put("re_user_name", userName);

        data.put("amount", amount.setScale(0, RoundingMode.HALF_UP).toString());
        data.put("desc", "找零工提现");
        //签名
        String s = WXPayUtil.generateSignature(data, key);
        data.put("sign", s);
        /** wxPay.transfer 这个方法中调用微信转账接口 */
        Map <String, String> resultMap = wxPay.transfer(data);
        log.info("获取传输参数:" + JSONUtils.toJSONString(data));

        // 下单失败，进行处理
        if (WXPayConstants.FAIL.equals(resultMap.get("return_code"))) {
            throw new RuntimeException("微信提现失败:" + resultMap.get("return_msg"));
        }

        if (WXPayConstants.FAIL.equals(resultMap.get("result_code"))) {
            //            if (WXPayConstants.SYSTEMERROR.equals(resultMap.get("err_code"))){
            //                cashWithdrawal(account,userName,withdrawalId,amount);
            //                return;
            //            }
            throw new RuntimeException("微信提现失败:" + resultMap.get("err_code_des"));
        }
    }



    /**
     * 订单查询
     *
     * @param outTradeNo
     *         商户交易号（系统交易的编号）
     */
    public String orderQuery (String outTradeNo) {

        try {
            WXPay wxPay = new WXPay(this.wxPaymentConfig);
            String appid = this.wxPaymentConfig.getAppID();
            Map <String, String> data = new HashMap <>();
            // 公众号id
            data.put("appid", appid);
            // 订单编号
            data.put("out_trade_no", outTradeNo);
            // 随机字符串
            data.put("nonce_str", WXPayUtil.generateNonceStr());

            // 订单查询
            Map <String, String> resultMap = wxPay.orderQuery(data);

            // 状态码
            String returnCode = resultMap.get("return_code");

            // 支付接口调用失败
            if (!WXPayConstants.SUCCESS.equals(returnCode)) {
                log.info("支付订单接口查询失败！{}", resultMap.get("return_msg"));
                return resultMap.get(resultMap.get("return_msg"));
            }

            // 业务结果
            String resultCode = resultMap.get("result_code");

            if (!WXPayConstants.SUCCESS.equals(resultCode)) {
                log.info("业务结果查询失败！{}, {}", resultMap.get("err_code"), resultMap.get("err_code_des"));
                return resultMap.get("err_code_des");
            }
        } catch (Exception e) {
            log.error("[微信]交易记录查询失败");
        }

        return "查询成功";
    }


    /**
     * 支付返回 支持app和小程序
     *
     * @param outTradeNo
     * @param flowTypeEnum
     * @param money
     * @param ip
     * @param createTime
     * @param tradeType
     * @param openId
     *
     * @return
     *
     * @throws Exception
     */
    public Map <String, String> payPlaceOrder (String outTradeNo, FlowTypeEnum flowTypeEnum, BigDecimal money,
            String ip, Date createTime, String tradeType, String openId, Long userId, String userName)
            throws Exception {
        // 原生支付


        //根据微信支付api来设置
        Map <String, String> data = new HashMap <>();
        WXPay wxPay = new WXPay(this.wxPaymentConfig);
        String key = this.wxPaymentConfig.getKey();
        String appid = this.wxPaymentConfig.getAppID();
        String mchid = this.wxPaymentConfig.getMchID();

        data.put("appid", appid);
        //商户号
        data.put("mch_id", mchid);
        //支付场景 APP 微信app支付 JSAPI 公众号支付  NATIVE 扫码支付
        data.put("trade_type", tradeType);
        if ("JSAPI".equals(tradeType)) {
            //JSAPI必须要openId
            log.debug("customXiaoAppid:" + this.wxPaymentConfig.getXcxAppid());
            data.put("appid", this.wxPaymentConfig.getXcxAppid());
            data.put("openid", openId);
        }
        //回调地址
        data.put("notify_url", this.wxPaymentConfig.getNotifyUrl());
        //终端ip
        data.put("spbill_create_ip", ip);
        // 测试默认1分钱
        //data.put("total_fee","1");
        //订单总金额
        data.put("total_fee", money.setScale(0, RoundingMode.HALF_UP).toString());
        //默认人民币
        data.put("fee_type", "CNY");
        //outTradeNo = flowTypeEnum.getValue() + "-" + outTradeNo + "-" + DateUtils.getNowDate().getTime();
        //交易号
        data.put("out_trade_no", outTradeNo);
        //记录交易号

        data.put("body", "找零工平台支付");
        data.put("device_info", "WEB");
        data.put("time_expire", DateUtils.parseDateToStr("yyyyMMddHHmmss", DateUtils.addMinutes(createTime, 29)));
        // 随机字符串小于32位
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        //签名
        String s = WXPayUtil.generateSignature(data, key);
        data.put("sign", s);
        // wxPay.unifiedOrder 这个方法中调用微信统一下单接口
        Map <String, String> respData = wxPay.unifiedOrder(data);

        log.info("获取传输参数:" + JSONUtils.toJSONString(data));
        log.info("获取预签名返回参数:" + JSONUtils.toJSONString(respData));

        if ("SUCCESS".equals(respData.get("return_code"))) {
            //返回给APP端的参数，APP端再调起支付接口
            Map <String, String> repData = new HashMap <>();

            if ("JSAPI".equals(tradeType)) {
                //JSAPI
                repData.put("appId", this.wxPaymentConfig.getXcxAppid());
                repData.put("package", "prepay_id=" + respData.get("prepay_id"));
                repData.put("nonceStr", respData.get("nonce_str"));
                repData.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
                repData.put("signType", WXPayConstants.MD5);
            } else {
                repData.put("appid", appid);
                repData.put("partnerid", mchid);
                repData.put("package", "Sign=WXPay");
                repData.put("prepayid", respData.get("prepay_id"));
                repData.put("noncestr", respData.get("nonce_str"));
                repData.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
            }
            log.debug("二次签名参数：" + JSONUtils.toJSONString(repData));
            //签名
            String sign = WXPayUtil.generateSignature(repData, key);
            repData.put("sign", sign);
            return repData;
        } else {
            log.info("预签名返回错误:" + JSONUtils.toJSONString(respData));
            throw new RuntimeException(respData.get("return_msg"));
        }
    }
}
